package moodish.dummy;

import java.util.LinkedList;

import moodish.comm.ServerComm;
import moodish.comm.ServerSideMessage;
import moodish.comm.ServerSideMessage.Type;
import moodish.server.MoodishServer;
import moodish.server.ServerGUI;

/**
 * Server engine communicates with clients through
 * {@link moodish.comm.ServerComm ServerComm}, receiving messages of type
 * {@link moodish.comm.ServerSideMessage ServerSideMessage} and answering to
 * with messages of type {@link moodish.comm.ClientSideMessage
 * ClientSideMessage}
 **/

public class MoodishServerDummy implements MoodishServer {
	private ServerComm serverComm;
	private LinkedList<User> online = new LinkedList<User>();
	private ServerGUI gui;

	/**
	 * Different types of error possible to occur when the server is running
	 * 
	 */
	private enum errorType {
		USER_ABSENT, USER_OFFLINE, MOOD_TOO_BIG, ALREADY_FRIEND, NOT_FRIEND
		// in future if we have more errors they will be added here
	}

	/**
	 * Loads the users informations and starts the communications and the server
	 * activity
	 */
	@Override
	public void start(ServerComm serverComm) {
		this.serverComm = serverComm;
		serverComm.start();
		gui = new ServerGUI();
		
		serve();
	}

	/**
	 * Server "working core", receiving messages from clients and processing
	 * them
	 */
	private void serve() {
		while (true) {
			ServerSideMessage newMessage = serverComm.getNextMessage();
			processMessage(newMessage);
		}
	}

	/**
	 * Processes the message received from user, throw network communication,
	 * based on it type
	 * 
	 * @param Message
	 *            received from client
	 */
	public void processMessage(ServerSideMessage message) {
		Type type = message.getType();

		switch (type) {
		case CLIENT_CONNECTED:
			processNewConnection(message);
			break;
		case CLIENT_DISCONNECTED:
			processDisconnection(message);
			break;
		case MOODISH_MESSAGE:
			processMood(message);
			break;
		case FRIENDSHIP:
			processFriendship(message);
			break;
		case UNFREINDSHIP:
			processUnfriendship(message);
			break;
		}
	}

	/**
	 * Executes the Unfriendship process triggered by a user
	 * 
	 * @param Message
	 *            received from client with the name of the friend to remove
	 */
	private void processUnfriendship(ServerSideMessage message) {
		String str = "UnfriendShip Message from: "
				+ message.getClientNickname() + " to unfriend: "
				+ message.getPayload();
		System.out.println(str);
		gui.addText(str);

		String clientName = message.getClientNickname();
		String friendName = message.getPayload();

		User client = null;
		User friend = null;
		for (User i : online) {
			if (i.getNickname().equals(clientName))
				client = i;
			if (i.getNickname().equals(friendName))
				friend = i;
		}

		if (client != null && friend != null) {
			if (client.isFriend(friend)) {
				client.rmFriend(friend);
				serverComm.sendNewUnfriendship(friendName, clientName);

				str = "Sent Unfriendship to: " + friendName + " with: "
						+ clientName;
				System.out.println(str);
				gui.addText(str);
			} else {
				dealWithErrors(clientName, friendName, errorType.NOT_FRIEND);
			}
		} else if (friend == null) {
			dealWithErrors(clientName, friendName, errorType.USER_OFFLINE);
		} else {
			dealWithErrors(clientName, null, errorType.USER_ABSENT);
		}
		System.out.println("----- End of Unfriendship Processing -----\n");
		gui.addText("----- End of Unfriendship Processing -----\n");
	}

	/**
	 * Executes the Friendship process triggered by a user
	 * 
	 * @param Message
	 *            received from client with the new friend name
	 */
	private void processFriendship(ServerSideMessage message) {
		String str = "FriendShip Message from: " + message.getClientNickname()
				+ " to friend: " + message.getPayload();
		System.out.println(str);
		gui.addText(str);

		String clientName = message.getClientNickname();
		String friendName = message.getPayload();

		User client = null;
		User friend = null;
		for (User i : online) {
			if (i.getNickname().equals(clientName))
				client = i;
			if (i.getNickname().equals(friendName))
				friend = i;
		}

		if (client != null && friend != null) {
			if (!client.isFriend(friend)) {
				client.addFriend(friend);
				serverComm.sendNewFriendship(friendName, clientName);
				str = "Sent FriendShip to: " + friendName + " with: "
						+ clientName;
				System.out.println(str);
				gui.addText(str);
			} else {
				dealWithErrors(clientName, friendName, errorType.ALREADY_FRIEND);
			}
		} else if (friend == null) {
			dealWithErrors(clientName, friendName, errorType.USER_OFFLINE);
		} else {
			dealWithErrors(clientName, null, errorType.USER_ABSENT);
		}

		System.out.println("----- End of Friendship Processing -----\n");
		gui.addText("----- End of Friendship Processing -----\n");
	}

	/**
	 * Executes the setting of a new mood by a user and the updating to the
	 * other users
	 * 
	 * @param Message
	 *            received from client with the new mood
	 */
	private void processMood(ServerSideMessage message) {
		String str = "Mood Message from: " + message.getClientNickname()
				+ " Containing: " + message.getPayload();
		System.out.println(str);
		gui.addText(str);
		String mood = message.getPayload();
		String clientName = message.getClientNickname();

		if (mood.length() <= 30) {

			User user = null;
			for (User i : online) {
				if (i.getNickname().equals(clientName)) {
					user = i;
					break;
				}
			}

			if (user != null) {
				user.setMood(message.getPayload());

				for (User i : online) {
					if (i.isFriend(user)) {
						serverComm.sendMoodishMessage(clientName,
								i.getNickname(), mood);
						str = "Sent Moodish Message to: " + i.getNickname()
								+ " with: " + mood + " Belonging to: "
								+ clientName;
						System.out.println(str);
						gui.addText(str);
					}
				}
			} else {
				dealWithErrors(clientName, null, errorType.USER_ABSENT);
			}
		} else {
			dealWithErrors(clientName, null, errorType.MOOD_TOO_BIG);
		}
		System.out.println("----- End of Mood Processing -----\n");
		gui.addText("----- End of Mood Processing -----\n");
	}

	/**
	 * Disconnects a client from the server and updates all the other clients
	 * and unfriends all clients form the disconnected user
	 * 
	 * @param Message
	 *            received from client with his username and the disconnection
	 *            flag
	 */
	private void processDisconnection(ServerSideMessage message) {
		String str = "Disconnection Message from: "
				+ message.getClientNickname();
		System.out.println(str);
		gui.addText(str);

		User user = null;
		for (User i : online) {
			if (i.getNickname().equals(message.getClientNickname())) {
				user = i;
				break;
			}
		}

		if (user != null) {
			online.remove(user);
			serverComm.disconnectClient(message.getClientNickname());

			for (User i : online) {
				i.rmFriend(user);
			}

			for (User i : online) {
				serverComm.sendClientDisconnected(i.getNickname(),
						user.getNickname());
				str = "Sent clientDisconnected to: " + i.getNickname()
						+ " with: " + user.getNickname();
				System.out.println(str);
				gui.addText(str);
			}
		} else {
			dealWithErrors(message.getClientNickname(), null,
					errorType.USER_ABSENT);
		}
		System.out.println("----- End of Disconnection Processing -----\n");
		gui.addText("----- End of Disconnection Processing -----\n");
	}

	/**
	 * Receives the request of register a new client, in case that the given
	 * username doesn't exists. In case it already exists, an error message is
	 * sent to the client noticing him that the username is repeated
	 * 
	 * @param Message
	 *            received from client with his username
	 */
	private void processNewConnection(ServerSideMessage message) {
		String str = "Connection Message from: " + message.getClientNickname();
		System.out.println(str);
		gui.addText(str);

		String clientName = message.getClientNickname();
		if (online.isEmpty()) {
			serverComm.sendClientConnected(clientName, clientName);
			str = "Sent clientConnected to: " + clientName + " with: "
					+ clientName;
			System.out.println(str);
			gui.addText(str);
		} else {
			for (User user : online) {
				serverComm.sendClientConnected(clientName, user.getNickname());
				str = "Sent clientConnected to: " + clientName + " with: "
						+ user.getNickname();
				System.out.println(str);
				gui.addText(str);
				serverComm.sendClientConnected(user.getNickname(), clientName);
				str = "Sent clientConnected to: " + user.getNickname()
						+ " with: " + clientName;
				System.out.println(str);
				gui.addText(str);
			}
		}

		online.add(new User(clientName));

		System.out.println("----- End of Connection Processing -----\n");
		gui.addText("----- End of Connection Processing -----\n");
	}

	/**
	 * Deals with an error related to a client
	 * 
	 * @param String
	 *            with Client Username
	 * @param Indication
	 *            of the error message type, of the type
	 *            {@link moodish.dummy.MoodishServerDummy.errorType errorType}
	 */
	private void dealWithErrors(String client, String friendName, errorType type) {
		String str;
		switch (type) {
		case USER_ABSENT:
			serverComm.sendError(client, "User " + client + " doesn't exist");

			// Something serious happened so we disconnect the client (in case
			// it exist on Comm side) to retry to connect normally
			serverComm.disconnectClient(client);
			break;
		case USER_OFFLINE:
			serverComm.sendError(client, "The user with nickname " + friendName
					+ " indicated is offline");

			// It is sent a message indicating the disconnection of the
			// currently offline client so it can update his clients list
			serverComm.sendClientDisconnected(client, friendName);
			str = "User Offline Error Sent";
			System.out.println(str);
			gui.addText(str);
			break;
		case MOOD_TOO_BIG:
			serverComm
					.sendError(client,
							"The indicated Mood is too big, please reduce the number of characters");
			str = "Mood Too Big Error Sent";
			System.out.println(str);
			gui.addText(str);
			break;
		case ALREADY_FRIEND:
			serverComm.sendError(client, "You are already friend of "
					+ friendName);
			str = "Already Friend Error Sent";
			System.out.println(str);
			gui.addText(str);
			break;
		case NOT_FRIEND:
			serverComm.sendError(client, "You are not a friend of "
					+ friendName);
			str = "Not Friend Error Sent";
			System.out.println(str);
			gui.addText(str);
			break;
		}
	}

	/**
	 * ServerComm Setter
	 * 
	 * @param serverComm
	 *            to set
	 */
	public void setServerComm(ServerComm serverComm) {
		this.serverComm = serverComm;
	}

}